package com.top.ruizhong.main.video

import android.annotation.SuppressLint
import android.os.CountDownTimer
import android.util.Rational
import android.view.Surface
import android.view.View
import androidx.camera.core.*
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.core.content.ContextCompat
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Observer
import com.google.common.util.concurrent.ListenableFuture
import com.module.lemlin.base.BaseAbstractFragment
import com.module.lemlin.base.BaseApplication
import com.top.ruizhong.R
import com.top.ruizhong.mode.manager.getDiskCacheDirectoryVideo
import kotlinx.android.synthetic.main.fragment_camera_screen.*
import java.io.File
import java.lang.StringBuilder
import java.lang.ref.WeakReference

class CameraScreenFragment(val onVideoSavedToActivity: (outputFileResults: VideoCapture.OutputFileResults) -> Unit) :
    BaseAbstractFragment() {

    companion object {

        //前置摄像头
        private val LENS_FACING_BACK: CameraSelector = CameraSelector.Builder()
            .requireLensFacing(CameraSelector.LENS_FACING_BACK)
            .build()

        //后置摄像头
        private val LENS_FACING_FRONT: CameraSelector = CameraSelector.Builder()
            .requireLensFacing(CameraSelector.LENS_FACING_FRONT)
            .build()

        private class VideoFileSaver(fragment: CameraScreenFragment) :
            VideoCapture.OnVideoSavedCallback {

            private var weakReference: WeakReference<CameraScreenFragment>? = null

            private val fileVideo: File
                get() = File(
                    getDiskCacheDirectoryVideo(BaseApplication.INSTANCE).path,
                    ("video_record_test.mp4")
                )
            private val mLock = Any()

            val isSaving: MutableLiveData<Boolean> = MutableLiveData()

            init {
                weakReference = WeakReference(fragment)
            }

            override fun onVideoSaved(outputFileResults: VideoCapture.OutputFileResults) {
                weakReference?.get()?.let {
                    it.onVideoSavedToActivity(outputFileResults)
                }
                synchronized(mLock) {
                    isSaving.postValue(false)
                }
            }

            override fun onError(videoCaptureError: Int, message: String, cause: Throwable?) {
                weakReference?.get()?.let {
                    it.toast(message)
                }
                synchronized(mLock) {
                    isSaving.postValue(false)
                }
            }

            fun getNewVideoOutputFileOptions(): VideoCapture.OutputFileOptions {
                return VideoCapture.OutputFileOptions.Builder(fileVideo).build()
            }

            fun isSaving(): Boolean {
                synchronized(mLock) {
                    return isSaving.value ?: false
                }
            }

            fun setSaving() {
                synchronized(mLock) {
                    isSaving.postValue(true)
                }
            }
        }

    }

    private var mUseCases: MutableList<UseCase>? = null

    private var mCamera: Camera? = null

    private var mCameraProvider: ProcessCameraProvider? = null

    private var mCameraSelector: CameraSelector = LENS_FACING_BACK

    private val mVideoFileSaver: VideoFileSaver by lazy {
        VideoFileSaver(fragment = this)
    }

    private val millisInFuture: Long = 16 * 1000
    private val countDownInterval: Long = 1000
    private val mCountDownTime: CountDownTimer by lazy {
        object : CountDownTimer(millisInFuture, countDownInterval) {
            @SuppressLint("RestrictedApi")
            override fun onFinish() {
                getVideoCapture()?.stopRecording()
            }

            override fun onTick(millisUntilFinished: Long) {
                val second: Long = (millisInFuture - millisUntilFinished) / 1000
                val secondStr: String = if (second.toString().length == 1)
                    "0${second}" else second.toString()
                tvCameraDownTime.text = StringBuilder()
                    .append("00:").append(secondStr)
            }
        }
    }

    override val layoutResId: Int
        get() = R.layout.fragment_camera_screen

    override fun initView() {
        initCameraProviderFuture()

        setUpFacingButton()
        setUpRecordButton()
    }

    override fun initData() {
        mVideoFileSaver.isSaving.observe(viewLifecycleOwner, Observer { isSaving ->
            setControlView(isSaving)
        })
    }

    override fun onDestroyView() {
        super.onDestroyView()
        mCameraProvider?.unbindAll()
    }

    private fun initCameraProviderFuture() {
        val cameraProviderFuture: ListenableFuture<ProcessCameraProvider> =
            ProcessCameraProvider.getInstance(requireContext())
        cameraProviderFuture.addListener(Runnable {
            mCameraProvider = cameraProviderFuture.get()
            bindUseCases()
        }, ContextCompat.getMainExecutor(requireContext()))
    }

    private fun bindUseCases() {
        mCameraProvider?.unbindAll()
        bindToLifecycleSafely(buildUseCases().apply { mUseCases = this })
        bindSurfaceProvider()
    }

    @SuppressLint("UnsafeExperimentalUsageError")
    private fun bindToLifecycleSafely(useCases: MutableList<UseCase>): Camera? {
        val viewPort: ViewPort = ViewPort.Builder(Rational(720, 1280), Surface.ROTATION_0)
            .setScaleType(ViewPort.FILL_END).build()
        val useCaseGroupBuilder: UseCaseGroup.Builder = UseCaseGroup.Builder()
        for (useCase: UseCase in useCases) {
            useCaseGroupBuilder.addUseCase(useCase)
        }
        mCamera = mCameraProvider?.bindToLifecycle(
            this as LifecycleOwner, mCameraSelector, useCaseGroupBuilder.build()
        )
        return mCamera
    }

    @SuppressLint("RestrictedApi")
    private fun buildUseCases(): MutableList<UseCase> {
        val userCases: MutableList<UseCase> = mutableListOf()

        val preview: Preview = Preview.Builder()
            .setTargetAspectRatio(AspectRatio.RATIO_16_9)
            .setTargetRotation(pvCameraPreview.display.rotation)
            .build()
        userCases.add(preview)

        val videoCapture: VideoCapture = VideoCapture.Builder()
            .setTargetAspectRatio(AspectRatio.RATIO_16_9)
            .setTargetRotation(pvCameraPreview.display.rotation)
            .setBitRate(1024 * 1024 * 3)
            .setVideoFrameRate(25)
            .build()
        userCases.add(videoCapture)

        return userCases
    }

    private fun bindSurfaceProvider() {
        getPreview()?.setSurfaceProvider(pvCameraPreview.surfaceProvider)
    }

    private fun setUpFacingButton() {
        ivCameraFacing.setOnClickListener {
            if (mCameraSelector == LENS_FACING_BACK) {
                mCameraSelector = LENS_FACING_FRONT
            } else if (mCameraSelector == LENS_FACING_FRONT) {
                mCameraSelector = LENS_FACING_BACK
            }
            bindUseCases()
        }
    }

    @SuppressLint("RestrictedApi")
    private fun setUpRecordButton() {
        ivCameraShoot.setOnClickListener {
            if (!mVideoFileSaver.isSaving()) {
                getVideoCapture()?.startRecording(
                    mVideoFileSaver.getNewVideoOutputFileOptions(),
                    ContextCompat.getMainExecutor(requireContext()), mVideoFileSaver
                )
                mVideoFileSaver.setSaving()
                mCountDownTime.start()
            } else if (mVideoFileSaver.isSaving()) {
                mCountDownTime.cancel()
                getVideoCapture()?.stopRecording()
            }
        }
    }

    private fun setControlView(isSaving: Boolean) {
        ivCameraShoot.setImageResource(
            if (isSaving) R.drawable.ic_record_start else R.drawable.ic_record_stop
        )
        ivCameraFacing.visibility =
            if (isSaving) View.GONE else View.VISIBLE
        tvCameraDownTime.text =
            if (isSaving) "00:00" else "点击录像"
    }

    private fun getPreview(): Preview? {
        return findUseCase(Preview::class.java)
    }

    private fun getVideoCapture(): VideoCapture? {
        return findUseCase(VideoCapture::class.java)
    }

    private fun <T : UseCase?> findUseCase(useCaseSubclass: Class<T>): T? {
        mUseCases?.forEach {
            if (useCaseSubclass.isInstance(it)) {
                return useCaseSubclass.cast(it)
            }
        }
        return null
    }

    private fun getCameraInfo(): CameraInfo? {
        return mCamera?.cameraInfo
    }

    private fun getCameraControl(): CameraControl? {
        return mCamera?.cameraControl
    }

}